﻿namespace Avatar.Controls
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    [TemplatePart(Name = ScrollViewer, Type = typeof(ScrollViewer))]
    [TemplatePart(Name = ContentPanel, Type = typeof(StackPanel))]
    public class PullList : ItemsControl
    {
        public const string ContentPanel = "ContentPanel";

        public const string ScrollViewer = "ScrollViewer";

        #region private members

        StackPanel contentPanel;

        ScrollViewer scrollViewer;

        const double Critical = 130.0;

        double offset;

        double limitation;

        bool isPulled;

        ManipulationAction action;

        #endregion

        /// <summary>
        /// Initializes a new instance of the PullList class.
        /// </summary>     
        public PullList()
        {
            DefaultStyleKey = typeof(PullList);
            action = ManipulationAction.None;
        }

        public override void OnApplyTemplate()
        {
            Touch.FrameReported += OnTouchFrameReported;

            scrollViewer = base.GetTemplateChild(ScrollViewer) as ScrollViewer;
            contentPanel = base.GetTemplateChild(ContentPanel) as StackPanel;
            contentPanel.Margin = new Thickness(0, -30, 0, 0);

            base.OnApplyTemplate();
        }

        void OnTouchFrameReported(object sender, TouchFrameEventArgs e)
        {
            TouchPoint touchPoint = null;
            TouchPointCollection touchPoints = e.GetTouchPoints(null);
            if (touchPoints != null && touchPoints.Count > 0)
            {
                touchPoint = touchPoints[0];
            }

            if (touchPoint == null)
            {
                return;
            }

            if (action == ManipulationAction.Started)
            {
                if (touchPoint.Action == TouchAction.Down)
                {
                    offset = touchPoint.Position.Y;
                    limitation = Critical + scrollViewer.VerticalOffset;
                    System.Diagnostics.Debug.WriteLine(string.Format("Started ({0})", touchPoint.Position));
                }

                if (!isPulled && touchPoint.Position.Y - offset > limitation)
                {
                    isPulled = true;
                    if (Pulled != null)
                    {
                        Pulled(this, EventArgs.Empty);
                        System.Diagnostics.Debug.WriteLine(string.Format("Pulled ({0})", touchPoint.Position));
                    }
                }
            }

            if (touchPoint.Action == TouchAction.Up)
            {
                var shouldReleased = isPulled;
                offset = 0;
                limitation = 0;
                isPulled = false;

                if (shouldReleased && Released != null)
                {
                    Released(this, EventArgs.Empty);
                    System.Diagnostics.Debug.WriteLine(string.Format("Released ({0})", touchPoint.Position));
                }
            }
        }

        #region PullPanelHeight DependencyProperty

        /// <summary>
        /// Gets or sets the PullPanel height.
        /// </summary>
        public int PullPanelHeight
        {
            get { return (int)GetValue(PullPanelHeightProperty); }
            set { SetValue(PullPanelHeightProperty, value); }
        }

        /// <summary>
        /// Identifies the PullPanelHeight dependency property.
        /// </summary>
        public static readonly DependencyProperty PullPanelHeightProperty =
            DependencyProperty.Register("PullPanelHeight", typeof(int), typeof(PullList), new PropertyMetadata(30, OnPullPanelHeightPropertyChanged));

        /// <summary>
        /// PullPanelHeightProperty changed handler.
        /// </summary>
        /// <param name="obj">The dependency object.</param>
        /// <param name="e">The event information.</param>
        private static void OnPullPanelHeightPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            PullList control = obj as PullList;
            int height = (int)e.NewValue;

            if (control.contentPanel != null)
            {
                control.contentPanel.Margin = new Thickness(0, -height, 0, 0);
            }
        }

        #endregion

        #region PullPanelContent DependencyProperty

        /// <summary>
        /// Gets or sets the content pull panel.
        /// </summary>
        public object PullPanel
        {
            get { return (object)GetValue(PullPanelProperty); }
            set { SetValue(PullPanelProperty, value); }
        }

        /// <summary>
        /// Identifies the PullPanel dependency property.
        /// </summary>
        public static readonly DependencyProperty PullPanelProperty =
            DependencyProperty.Register("PullPanel", typeof(object), typeof(PullList), new PropertyMetadata(null, OnPullPanelPropertyChanged));

        /// <summary>
        /// PullPanelProperty changed handler.
        /// </summary>
        /// <param name="obj">The dependency object.</param>
        /// <param name="e">The event information.</param>
        private static void OnPullPanelPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region PullPanelTemplate DependencyProperty

        /// <summary>
        /// Gets or sets the data template that defines
        /// the content information.
        /// </summary>
        public DataTemplate PullPanelTemplate
        {
            get { return (DataTemplate)GetValue(PullPanelTemplateProperty); }
            set { SetValue(PullPanelTemplateProperty, value); }
        }

        /// <summary>
        /// Identifies the PullPanelTemplateProperty dependency property.
        /// </summary>
        public static readonly DependencyProperty PullPanelTemplateProperty =
            DependencyProperty.Register("PullPanelTemplateProperty", typeof(DataTemplate), typeof(PullList), new PropertyMetadata(null, OnPullPanelTemplatePropertyChanged));

        /// <summary>
        /// PullPanelTemplate changed handler.
        /// </summary>
        /// <param name="obj">The dependency object.</param>
        /// <param name="e">The event information.</param>
        private static void OnPullPanelTemplatePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region Events

        public event EventHandler Pulled;

        public event EventHandler Released;

        #endregion

        #region ItemsControl overriden methods

        /// <summary>
        /// Overrides the DependencyObject used by this ItemsControl
        /// to be a PullListItem.
        /// </summary>
        /// <returns>
        /// A new instance of a PullListItem.
        /// </returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            PullListItem item = new PullListItem();
            item.ManipulationStateChanged += OnPullListItemManipulationStateChanged;
            return item;
        }

        void OnPullListItemManipulationStateChanged(object sender, ManipulationActionEventArgs e)
        {
            action = e.Action;
        }

        /// <summary>
        /// Acknowledges an item as being of the same type as its container
        /// if it is a PullListItem.
        /// </summary>
        /// <param name="item">An item owned by the ItemsControl.</param>
        /// <returns>True if the item is a PullListItem.</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is PullListItem);
        }

        #endregion
    }
}
